/* $RCSFile: StringResourcesDALC.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.ExternalResourceProvider/StringResourcesDALC.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:30  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Configuration;
using System.Collections.Specialized;
using System.Globalization;
using System.Threading;
using System.Data;
using System.Runtime.Remoting.Contexts;
using System.Runtime.CompilerServices;
using System.Diagnostics;

using ADNCRM.CustomResourceProviders;


namespace ADNCRM.CustomResourceProviders.DBResourceProvider
{
    /// <summary>
    /// Data access component for the StringResources table. 
    /// This type is thread safe.
    /// </summary>
    public class StringResourcesDALC: IDisposable
    {
        private string m_defaultResourceCulture =   "en";
        private string m_resourceType           =   string.Empty;
        private string m_connectionString       =   "ADNCRM_ConnectionString";

        private SqlConnection m_connection;
        private SqlCommand m_cmdGetResourceByCultureAndKey;
        private SqlCommand m_cmdGetResourcesByCulture;

        /// <summary>
        /// Constructs this instance of the data access 
        /// component supplying a resource type for the instance. 
        /// </summary>
        /// <param name="resourceType">The resource type.</param>
        public StringResourcesDALC(string connectionSt,string resourceType)
        {
            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC Ctor({0})", resourceType),ResourceTraceSwitch.Sw.Info);
            // save the resource type for this instance
            m_resourceType                              =   resourceType;
            if ( !string.IsNullOrEmpty(connectionSt ) )
                m_connectionString                      =   connectionSt;

            // grab the connection string
            m_connection                                =   new SqlConnection(ConfigurationManager.ConnectionStrings[m_connectionString].ConnectionString);

            // command to retrieve the resource the matches 
            // a specific type, culture and key
            m_cmdGetResourceByCultureAndKey             =   new SqlCommand("SELECT resourceType, cultureCode, resourceKey, resourceValue FROM dbo.adn_StringResources WHERE (resourceType=@resourceType) AND (cultureCode=@cultureCode) AND (resourceKey=@resourceKey)");
            m_cmdGetResourceByCultureAndKey.Connection  =   m_connection;
            m_cmdGetResourceByCultureAndKey.Parameters.AddWithValue("resourceType", m_resourceType);
            m_cmdGetResourceByCultureAndKey.Parameters.AddWithValue("cultureCode", string.Empty);
            m_cmdGetResourceByCultureAndKey.Parameters.AddWithValue("resourceKey", string.Empty);

            // command to retrieve all resources for a particular culture
            m_cmdGetResourcesByCulture                  =   new SqlCommand("SELECT resourceType, cultureCode, resourceKey, resourceValue FROM dbo.adn_StringResources WHERE (resourceType=@resourceType) AND (cultureCode=@cultureCode)");
            m_cmdGetResourcesByCulture.Connection       =   m_connection;
            m_cmdGetResourcesByCulture.Parameters.AddWithValue("resourceType", m_resourceType);
            m_cmdGetResourcesByCulture.Parameters.AddWithValue("cultureCode", string.Empty);

        }

        /// <summary>
        /// Uses an open database connection to recurse 
        /// looking for the resource.
        /// Retrieves a resource entry based on the 
        /// specified culture and resource 
        /// key. The resource type is based on this instance of the
        /// StringResourceDALC as passed to the constructor.
        /// Resource fallback follows the same mechanism 
        /// of the .NET 
        /// ResourceManager. Ultimately falling back to the 
        /// default resource
        /// specified in this class.
        /// </summary>
        /// <param name="culture">The culture to search with.</param>
        /// <param name="resourceKey">The resource key to find.</param>
        /// <returns>If found, the resource string is returned. 
        /// Otherwise an empty string is returned.</returns>
        private string GetResourceByCultureAndKeyInternal(CultureInfo culture, string resourceKey)
        {

            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourceByCultureAndKeyInternal BEG ({0},{1})", (culture == null) ? string.Empty:culture.TwoLetterISOLanguageName, resourceKey),ResourceTraceSwitch.Sw.Info);
            // we should only get one back, but just in case, we'll iterate reader results
            StringCollection resources      =   new StringCollection();
            string resourceValue            =   null;

            // set up the dynamic query params
            this.m_cmdGetResourceByCultureAndKey.Parameters["cultureCode"].Value = culture.Name;
            this.m_cmdGetResourceByCultureAndKey.Parameters["resourceKey"].Value = resourceKey;

            // get resources from the database
            using (SqlDataReader reader     =   m_cmdGetResourceByCultureAndKey.ExecuteReader())
            {
                while (reader.Read())
                {
                    resources.Add(reader.GetString(reader.GetOrdinal("resourceValue")));
                }
            }

            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourceByCultureAndKeyInternal resources.Count: {0}, m_defaultResourceCulture: {1}", resources.Count,m_defaultResourceCulture),ResourceTraceSwitch.Sw.Info);
            // we should only get 1 back, this is just to verify the tables aren't incorrect
            if (resources.Count == 0)
            {
                // is this already fallback location?
                if (culture.Name == m_defaultResourceCulture)
                {
                    throw new InvalidOperationException(String.Format(Thread.CurrentThread.CurrentUICulture, Properties.Resources.RM_DefaultResourceNotFound, resourceKey));
                }

                // try to get parent culture
                culture                     =   culture.Parent;
                if (culture.Name.Length == 0)
                {
                    // there isn't a parent culture, change to neutral
                    culture                 =   new CultureInfo(this.m_defaultResourceCulture);
                }
                resourceValue               =   GetResourceByCultureAndKeyInternal(culture, resourceKey);
            }
            else if (resources.Count == 1)
            {
                resourceValue               =   resources[0];
            }
            else
            {
                // if > 1 row returned, log an error, we shouldn't have > 1 value for a resourceKey!
                throw new DataException(String.Format(Thread.CurrentThread.CurrentUICulture, Properties.Resources.RM_DuplicateResourceFound, resourceKey));
            }

            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourceByCultureAndKeyInternal END resourceValue: {0}", resourceValue),ResourceTraceSwitch.Sw.Info);
            return resourceValue;
        }

        /// <summary>
        /// Returns a dictionary type containing all resources for a 
        /// particular resource type and culture.
        /// The resource type is based on this instance of the
        /// StringResourceDALC as passed to the constructor.
        /// </summary>
        /// <param name="culture">The culture to search for.</param>
        /// <param name="resourceKey">The resource key to 
        /// search for.</param>
        /// <returns>If found, the dictionary contains key/value 
        /// pairs for each 
        /// resource for the specified culture.</returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public ListDictionary GetResourcesByCulture(CultureInfo culture)
        {
            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourcesByCulture BEG culture: {0}", (culture == null) ? string.Empty:culture.TwoLetterISOLanguageName),ResourceTraceSwitch.Sw.Info);
            // make sure we have a default culture at least
            if (culture == null || culture.Name.Length == 0)
            {
                culture                                                     =   new CultureInfo(m_defaultResourceCulture);
            }

            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourcesByCulture culture: {0}", (culture == null) ? string.Empty:culture.TwoLetterISOLanguageName),ResourceTraceSwitch.Sw.Info);
            // set up dynamic query string parameters
            m_cmdGetResourcesByCulture.Parameters["cultureCode"].Value      =   culture.Name;

            // create the dictionary
            ListDictionary resourceDictionary                               =   new ListDictionary();

            // open a connection to gather resource and create the dictionary
            try
            {
                m_connection.Open();

                // get resources from the database
                using (SqlDataReader reader = this.m_cmdGetResourcesByCulture.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        string k                                            =   reader.GetString(reader.GetOrdinal("resourceKey"));
                        string v                                            =   reader.GetString(reader.GetOrdinal("resourceValue"));
                        resourceDictionary.Add(k, v);
                    }
                }

            }
            finally
            {
                m_connection.Close();
            }
            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourcesByCulture END resourceDictionary.Count: {0}", resourceDictionary.Count),ResourceTraceSwitch.Sw.Info);
            // TODO: check dispose results
            return resourceDictionary;
        }

        /// <summary>
        /// Retrieves a resource entry based on the specified culture and 
        /// resource key. The resource type is based on this instance of the
        /// StringResourceDALC as passed to the constructor.
        /// To optimize performance, this function opens the database connection 
        /// before calling the internal recursive function. 
        /// </summary>
        /// <param name="culture">The culture to search with.</param>
        /// <param name="resourceKey">The resource key to find.</param>
        /// <returns>If found, the resource string is returned. Otherwise an empty string is returned.</returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public string GetResourceByCultureAndKey(CultureInfo culture, string resourceKey)
        {
            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourceByCultureAndKey BEG ({0},{1})", (culture == null) ? string.Empty:culture.TwoLetterISOLanguageName, resourceKey),ResourceTraceSwitch.Sw.Info);
            string resourceValue        =   string.Empty;

            try
            {

                // make sure we have a default culture at least
                if (culture == null || culture.Name.Length == 0)
                {
                    culture             =   new CultureInfo(this.m_defaultResourceCulture);
                }

                // open the connection before we call the recursive reading function
                m_connection.Open();

                // recurse to find resource, includes fallback behavior
                resourceValue           =   GetResourceByCultureAndKeyInternal(culture, resourceKey);
            }
            finally
            {
                // cleanup the connection, reader won't do that if it was open prior to calling in, and that's what we wanted
                m_connection.Close();
            }
            if ( ResourceTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "[V]StringResourcesDALC GetResourceByCultureAndKey END resourceValue: {0}", resourceValue),ResourceTraceSwitch.Sw.Info);
            return resourceValue;
        }

        #region IDisposable Members

            public void  Dispose()
            {
                try
                {
                    // TODO: add in idisposable pattern, check what we're cleaning up here
                    this.m_cmdGetResourceByCultureAndKey.Dispose();
                    this.m_cmdGetResourcesByCulture.Dispose();
                    this.m_connection.Dispose();
                }
                catch { }
            }

        #endregion
    }

    
}
